#include <QAction>
#include <QGridLayout>
#include <QMenu>
#include <QMessageBox>
#include <QSplitter>
#include <QToolBar>
#include <QToolButton>
#include "about.h"
#include "abstract-database-client.h"
#include "abstract-section-model.h"
#include "allow-remove-data-settings.h"
#include "csv-manager-dialog.h"
#include "graph-node.h"
#include "icon-store.h"
#include "model-helper.h"
#include "navigator-bar.h"
#include "section-descendant-view.h"
#include "section-info.h"
#include "settings-dialog.h"
#include "sort-filter-proxy-model.h"
#include "standard-data-addition-dialog.h"
#include "standard-database-model.h"
#include "standard-section-view.h"
#include "table-view.h"
#include "value-list-result.h"

StandardSectionView::StandardSectionView(QWidget *parent) :
    AbstractSectionView(parent), m_tableView(0), m_sectionDescendantView(0), m_toolBar(0),
    m_verticalLayout(0), m_splitter(0), m_sectionModel(0), m_proxyModel(0), m_addDataAction(0),
    m_editDataAction(0), m_removeDataAction(0), m_enterIntoDataAction(0),
    m_sectionDescendantViewEnabled(true), m_tableViewEnabled(true), m_databaseClientEnabled(true),
    m_addDataEnabled(true), m_editDataEnabled(true), m_removeDataEnabled(true)
{
    m_noRowSelectedError = "Żaden wiersz nie jest zaznaczony.";
    m_currentIndexNotSelectedError = "Bieżąca komórka nie jest zaznaczona.";
}

void StandardSectionView::initialize()
{
    setCentralWidget(new QWidget(this));
    m_verticalLayout = new QVBoxLayout(centralWidget());
    m_verticalLayout->addWidget(m_navigatorBar);
    m_navigatorBar->setParent(centralWidget());

    if (m_sectionDescendantViewEnabled)
    {
        m_sectionDescendantView = new SectionDescendantView(this);
        m_sectionDescendantView->setSectionNode(m_sectionNode);
        m_sectionDescendantView->initialize();
        connect (m_sectionDescendantView, &SectionDescendantView::sectionChosen,
                 this, &StandardSectionView::descendantViewItemActivated);
    }

    if (m_tableViewEnabled)
    {
        m_tableView = new TableView(this);
        m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
        m_tableView->setSortingEnabled(true);
        m_tableView->setHorizontalScrollMode(QTableView::ScrollPerPixel);
    }

    if (m_sectionDescendantViewEnabled)
    {
        m_splitter = new QSplitter(Qt::Horizontal, this);
        m_splitter->setChildrenCollapsible(false);
        m_splitter->addWidget(m_sectionDescendantView);
        m_splitter->setStretchFactor(0, 1);
        m_verticalLayout->addWidget(m_splitter);
        if (m_tableViewEnabled)
        {
            m_splitter->addWidget(m_tableView);
            m_splitter->setStretchFactor(0, 15);
            m_splitter->setStretchFactor(1, 50);
        }
    }
    else
    {
        if (m_tableViewEnabled)
        {
            m_verticalLayout->addWidget(m_tableView);
        }
    }

    m_toolBar = new QToolBar(this);
    m_toolBar->setMovable(false);
    m_toolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    addToolBar(m_toolBar);

    QToolButton* mainMenuAction = new QToolButton(this);
    mainMenuAction->setIcon(IconStore::icon(IconStore::SystemRun, m_toolBarIconSize));
    mainMenuAction->setIconSize(QSize(m_toolBarIconSize, m_toolBarIconSize));
    mainMenuAction->setText("Menu");
    mainMenuAction->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    mainMenuAction->setPopupMode(QToolButton::InstantPopup);
    QMenu* mainMenuActionMenu = new QMenu(this);
    mainMenuAction->setMenu(mainMenuActionMenu);
    m_toolBar->addWidget(mainMenuAction);

    QAction* openSettingsAction = new QAction("Ustawienia", this);
    openSettingsAction->setIcon(IconStore::icon(IconStore::PreferencesSystem, m_menuActionIconSize));
    connect (openSettingsAction, &QAction::triggered, this, &StandardSectionView::openSettingsDialog);
    mainMenuActionMenu->addAction(openSettingsAction);

    QAction* exportDataAction = new QAction("Eksportuj dane", this);
    exportDataAction->setIcon(IconStore::icon(IconStore::DocumentExport, m_menuActionIconSize));
    connect (exportDataAction, &QAction::triggered, this, &StandardSectionView::exportData);
    mainMenuActionMenu->addAction(exportDataAction);


    QAction* aboutSoftware = new QAction("O programie", this);
    aboutSoftware->setIcon(QIcon(":/images/28x28/dialog-hint.png"));
    connect(aboutSoftware, &QAction::triggered, this, &StandardSectionView::about);
    mainMenuActionMenu->addAction(aboutSoftware);


    if (!m_tableViewEnabled)
        return;

    m_addDataAction = new QAction(IconStore::icon(IconStore::ListAdd, m_toolBarIconSize), m_addDataText, this);
    m_editDataAction = new QAction(IconStore::icon(IconStore::DocumentEdit, m_toolBarIconSize), m_editDataText, this);
    m_editDataAction->setCheckable(true);
    m_removeDataAction = new QAction(IconStore::icon(IconStore::ListRemove, m_toolBarIconSize), m_removeDataText, this);
    connect (m_addDataAction, &QAction::triggered, this, &StandardSectionView::addData);
    connect (m_editDataAction, &QAction::toggled, this, &StandardSectionView::editData);
    connect (m_removeDataAction, &QAction::triggered, this, &StandardSectionView::removeData);
    m_toolBar->addAction(m_addDataAction);
    m_toolBar->addAction(m_editDataAction);
    m_toolBar->addAction(m_removeDataAction);
    if (m_sectionNode->hasChildNodes())
    {
        m_enterIntoDataAction = new QAction(IconStore::icon(IconStore::ArrowRight, m_toolBarIconSize), m_enterIntoDataText, this);
        connect (m_enterIntoDataAction, &QAction::triggered, this, &StandardSectionView::enterIntoData);
        m_toolBar->addAction(m_enterIntoDataAction);
    }

    m_sectionModel->setSectionInfo(m_sectionNode->data());
    connect (m_sectionModel, &AbstractSectionModel::initialized, this, &StandardSectionView::continueInitialize);
    connect (m_sectionModel, &AbstractSectionModel::notifyHint, this, &StandardSectionView::notifyHint);
    connect (m_sectionModel, &AbstractSectionModel::notifyInformation, this, &StandardSectionView::notifyInformation);
    connect (m_sectionModel, &AbstractSectionModel::notifyWarning, this, &StandardSectionView::notifyWarning);
    m_sectionModel->initialize();
}

void StandardSectionView::continueInitialize()
{
    m_addDataAction->setEnabled(m_addDataEnabled);
    m_editDataAction->setEnabled(m_editDataEnabled);
    m_allowRemoveDataSettings = AllowRemoveDataSettings::instance();
    connect (m_allowRemoveDataSettings.data(), &AllowRemoveDataSettings::allowRemoveChanged, this, &StandardSectionView::changeAllowRemoveData);
    m_removeDataAction->setEnabled(m_removeDataEnabled || m_allowRemoveDataSettings->isRemovingAllowed());

    connect (m_sectionModel, &AbstractSectionModel::loadDataFinished, this, &StandardSectionView::finishInitialize);
    connect (m_sectionModel, &AbstractSectionModel::delegatesAboutToBeChanged, this, &StandardSectionView::clearDelegates);
    connect (m_sectionModel, &AbstractSectionModel::delegatesChanged, this, &StandardSectionView::setDelegates);
    m_sectionModel->loadData();
}

void StandardSectionView::finishInitialize()
{
    m_proxyModel = new SortFilterProxyModel(this);
    m_proxyModel->setSourceModel(m_sectionModel);
    m_tableView->setModel(m_proxyModel);
    setDelegates();
    connect (m_tableView, &TableView::activated, this, &StandardSectionView::tableViewItemActivated);
}

void StandardSectionView::changeAllowRemoveData (bool allowed)
{
    m_removeDataAction->setEnabled(m_removeDataEnabled || allowed);
}

void StandardSectionView::setDelegates()
{
    QMap<int,QAbstractItemDelegate*> delegates = m_sectionModel->delegates();
    QMapIterator<int,QAbstractItemDelegate*> it(delegates);
    while (it.hasNext())
    {
        it.next();
        m_tableView->setItemDelegateForColumn(it.key(), it.value());
    }
}

void StandardSectionView::clearDelegates()
{
    for (int i = 0; i < m_sectionModel->columnCount(); i++)
        m_tableView->setItemDelegateForColumn(i, 0);
}

void StandardSectionView::refresh()
{
    if (m_sectionModel)
        m_sectionModel->loadData();
}

void StandardSectionView::setDescendantViewEnabled(bool enabled)
{
    m_sectionDescendantViewEnabled = enabled;
}

void StandardSectionView::setFreezableTableViewEnabled(bool enabled)
{
    m_tableViewEnabled = enabled;
}

void StandardSectionView::setDatabaseClientEnabled(bool enabled)
{
    m_databaseClientEnabled = enabled;
}

void StandardSectionView::addData()
{
    StandardDataAdditionDialog* dialog = new StandardDataAdditionDialog(this);
    connect (dialog, &StandardDataAdditionDialog::addDataRequested, m_sectionModel, &AbstractSectionModel::addData);
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->setWindowTitle(m_addDataText);
    dialog->setModel(m_sectionModel);
    dialog->initialize();
    dialog->open();
}

void StandardSectionView::editData (bool enabled)
{
    m_sectionModel->setEditingEnabled(enabled);
}

void StandardSectionView::removeData()
{
    if (!canRemoveData())
        return;

    QMessageBox* messageBox = new QMessageBox(this);
    messageBox->setWindowTitle(m_removeDataText);
    messageBox->setIcon(QMessageBox::Question);
    messageBox->setText(tr(m_removeDataConfirmation.toUtf8().constData(), "",
                           ModelHelper::rowsInSelection(m_tableView)));
    // bug in Qt 4.8 - result depends on button insertion order
    messageBox->addButton("Anuluj", QMessageBox::RejectRole);
    messageBox->addButton("Usuń", QMessageBox::AcceptRole);
    messageBox->setAttribute(Qt::WA_DeleteOnClose);
    messageBox->open(this, SLOT(removeDataFinished(int)));
}

void StandardSectionView::enterIntoData()
{
    if (!m_tableView->selectionModel()->hasSelection())
    {
        emit notifyHint(m_noRowSelectedError, "Aby wejść do danych, zaznacz jakiś wiersz.");
        return;
    }
    else if (!ModelHelper::isCurrentIndexSelected(m_tableView))
    {
        emit notifyHint(m_currentIndexNotSelectedError, "Aby wejść do danych, bieżąca komórka musi być zaznaczona.");
        return;
    }

    Q_ASSERT(m_sectionNode->hasChildNodes());
    GraphNode<SectionInfo>* sectionNode = m_sectionNode->childNodes().at(0);
    sectionNode->data().setLoadingParameters(nextSectionParameters());
    emit showSectionRequested(sectionNode, false);
}

void StandardSectionView::exportData()
{
    CsvManagerDialog* dialog = new CsvManagerDialog(this);
    dialog->setDialogMode(CsvManagerDialog::ExportMode);
    dialog->setTableView(m_tableView);
    dialog->initialize();
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    connect (dialog, &CsvManagerDialog::notifyInformation, this, &StandardSectionView::notifyInformation);
    connect (dialog, &CsvManagerDialog::notifyWarning, this, &StandardSectionView::notifyWarning);
    dialog->show();
}

void StandardSectionView::openSettingsDialog()
{
    SettingsDialog* dialog = new SettingsDialog(this);
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->initialize();
    connect (dialog, &SettingsDialog::notifyHint, this, &StandardSectionView::notifyHint);
    connect (dialog, &SettingsDialog::notifyInformation, this, &StandardSectionView::notifyInformation);
    connect (dialog, &SettingsDialog::notifyWarning, this, &StandardSectionView::notifyWarning);
    dialog->show();
}

bool StandardSectionView::canRemoveData()
{
    if (!m_tableView->selectionModel()->hasSelection())
    {
        emit notifyHint(m_noRowSelectedError, "Aby usunąć dane, zaznacz jakiś wiersz.");
        return false;
    }
    else if (!ModelHelper::isCurrentIndexSelected(m_tableView))
    {
        emit notifyHint(m_currentIndexNotSelectedError, "Aby usunąć dane, bieżąca komórka musi być zaznaczona.");
        return false;
    }

    return true;
}

void StandardSectionView::removeDataFinished (int result)
{
    if (result != QDialog::Accepted)
        return;

    Q_ASSERT(m_tableView->selectionBehavior() == QAbstractItemView::SelectRows);
    QVariantList ids = m_sectionModel->ids(ModelHelper::normalSelectedRows(m_tableView));
    Q_ASSERT(ids.size());
    m_sectionModel->removeData(ids);
}

void StandardSectionView::descendantViewItemActivated (GraphNode<SectionInfo>* sectionNode)
{
    sectionNode->data().setLoadingParameters(m_sectionNode->data().loadingParameters());
    emit showSectionRequested(sectionNode, true);
}

void StandardSectionView::tableViewItemActivated (const QModelIndex &index)
{
    Q_UNUSED(index);
    if (m_sectionModel->isEditingEnabled())
        return;

    if (!m_sectionNode->hasChildNodes())
        return;

    GraphNode<SectionInfo>* sectionNode = m_sectionNode->childNodes().at(0);
    sectionNode->data().setLoadingParameters(nextSectionParameters());
    emit showSectionRequested(sectionNode, false);
}

void StandardSectionView::about()
{
  About about;
  about.exec();
}

QVariantList StandardSectionView::nextSectionParameters()
{
    if (!m_tableView)
        return QVariantList();

    if (!m_tableView->selectionModel()->hasSelection())
        return QVariantList();

    if (!ModelHelper::isOnlyOneRowSelected(m_tableView))
        return QVariantList();

    return QVariantList() << m_sectionModel->id(ModelHelper::normalSelectedRow(m_tableView));
}
